#include "ConvertMotionHandlerDialog.h"
#include "ui_ConvertMotionHandlerDialog.h"

#include <MMM/FactoryPluginLoader.h>
#include <MMM/XMLTools.h>
#include <MMM/Model/ModelReaderXML.h>

#include "QFileDialog"
#include "QMessageBox"
#include "ConvertingMotionHandlerDialog.h"

ConvertMotionHandlerDialog::ConvertMotionHandlerDialog(QWidget* parent)
    : QDialog(parent),
      ui(new Ui::ConvertMotionHandlerDialog)
{
    ui->setupUi(this);

    connect(ui->chooseMotionBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentMotion(int)));
    connect(ui->loadTargetModelButton, SIGNAL(clicked()), this, SLOT(loadTargetModel()));
    connect(ui->loadConverterConfigButton, SIGNAL(clicked()), this, SLOT(loadConverterConfiguration()));
    connect(ui->convertMotionButton, SIGNAL(clicked()), this, SLOT(convertMotion()));
    connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(close()));

    loadTargetModel(settings.value("converter/modelfilepath", "").toString().toStdString(), true);
    loadConverterConfiguration(settings.value("converter/configfilepath", "").toString().toStdString());
}

ConvertMotionHandlerDialog::~ConvertMotionHandlerDialog()
{
    delete ui;
}

void ConvertMotionHandlerDialog::loadMotions(MMM::MotionList motions) {
    ui->chooseMotionBox->clear();
    convertedMotion = nullptr;
    currentMotion = nullptr;
    this->motions = motions;

    int i = 0;
    for (MMM::MotionPtr motion : motions) {
        ui->chooseMotionBox->addItem(QString::fromStdString(motion->getName()));
        if (motion->getName() == motionName) ui->chooseMotionBox->setCurrentIndex(i);
        i++;
    }
    if (!currentMotion) setCurrentMotion(0);
}

MMM::MotionPtr ConvertMotionHandlerDialog::convertMotion(MMM::MotionList motions) {
    loadMotions(motions);
    exec();
    return convertedMotion;
}

void ConvertMotionHandlerDialog::setConvertedMotion(MMM::MotionPtr motion) {
    convertedMotion = motion;
    if (convertedMotion) this->close();
}

void ConvertMotionHandlerDialog::reject() {
    this->hide();
}

void ConvertMotionHandlerDialog::setCurrentMotion(int index) {
    if (index >= 0 && motions.size() > static_cast<std::size_t>(index)) {
        currentMotion = motions[index];
        motionName = currentMotion->getName();
        setConverterOptions();
    }
    enableConvertMotionButton();
}

void ConvertMotionHandlerDialog::loadTargetModel() {
    std::string modelFilePath = QFileDialog::getOpenFileName(this, tr("Load target model"), settings.value("converter/modelfilesearchpath", "").toString(), tr("XML files (*.xml)")).toStdString();
    settings.setValue("converter/modelfilesearchpath", QString::fromStdString(MMM::XML::getPath(modelFilePath)));
    loadTargetModel(modelFilePath);
}

void ConvertMotionHandlerDialog::loadTargetModel(const std::string &modelFilePath, bool ignoreError) {
    if (!modelFilePath.empty()) {
        MMM::ModelReaderXMLPtr r(new MMM::ModelReaderXML());
        targetModel = r->loadModel(modelFilePath);
        if (!targetModel && !ignoreError) {
            QMessageBox* msgBox = new QMessageBox(this);
            std::string error_message = "Could not read model from " + modelFilePath + "!";
            msgBox->setText(QString::fromStdString(error_message));
            std::cout << error_message << std::endl;
            msgBox->exec();
        }
        setTargetModelLabel(modelFilePath);
        settings.setValue("converter/modelfilepath", QString::fromStdString(modelFilePath));
        enableConvertMotionButton();
    }
}

void ConvertMotionHandlerDialog::loadConverterConfiguration() {
    std::string converterConfigFilePath = QFileDialog::getOpenFileName(this, tr("Load converter configuration"), settings.value("converter/configfilepath", "").toString(), tr("XML files (*.xml)")).toStdString();
    loadConverterConfiguration(converterConfigFilePath);
}

void ConvertMotionHandlerDialog::loadConverterConfiguration(const std::string &converterConfigFilePath) {
    if (!converterConfigFilePath.empty()) {
        this->converterConfigFilePath = converterConfigFilePath;
        setConverterConfigLabel(converterConfigFilePath);
        settings.setValue("converter/configfilepath", QString::fromStdString(converterConfigFilePath));
        enableConvertMotionButton();
    }
}

void ConvertMotionHandlerDialog::update(std::map<std::string, MMM::MotionConverterFactoryPtr > motionConverterFactories) {
    this->motionConverterFactories = motionConverterFactories;

    ui->chooseConverterBox->clear();
    for (const auto &converter : motionConverterFactories) ui->chooseConverterBox->addItem(QString::fromStdString(converter.first));
    enableConvertMotionButton();
}

void ConvertMotionHandlerDialog::convertMotion() {
    MMM::ModelPtr targetModelProcessed = targetModel;
    MMM::ModelProcessorWinterPtr targetModelProcessor = getModelProcessor();
    if (targetModelProcessor) targetModelProcessed = targetModelProcessor->convertModel(targetModel);

    try {
        MMM::MotionConverterPtr motionConverter = getMotionConverterFactory()->createMotionConverter(currentMotion, targetModelProcessed, targetModel, targetModelProcessor, converterConfigFilePath, getMotionName());
        ConvertingMotionHandlerDialog* convertingDialog = new ConvertingMotionHandlerDialog(this, motionConverter, currentMotion);
        connect(convertingDialog, SIGNAL(motionConverted(MMM::MotionPtr)), this, SLOT(setConvertedMotion(MMM::MotionPtr)));
        convertingDialog->open();
    } catch (MMM::Exception::MMMException &e) {
        QMessageBox* msgBox = new QMessageBox(this);
        msgBox->setText(e.what());
        std::cout << e.what() << std::endl;
        msgBox->exec();
    }
}

bool ConvertMotionHandlerDialog::enableConvertMotionButton() {
    if (targetModel && !converterConfigFilePath.empty() && currentMotion && motionConverterFactories.size() > 0) {
        ui->convertMotionButton->setEnabled(true);
        return true;
    }
    ui->convertMotionButton->setEnabled(false);
    return false;
}

MMM::ModelProcessorWinterPtr ConvertMotionHandlerDialog::getModelProcessor() {
    MMM::ModelProcessorWinterPtr targetModelProcessor;
    if (ui->winterProcessorGroupBox->isChecked()) {
        targetModelProcessor = MMM::ModelProcessorWinterPtr(new MMM::ModelProcessorWinter());
        targetModelProcessor->setup(ui->heightSpinner->value(), ui->weightSpinner->value());
    }
    return targetModelProcessor;
}

MMM::MotionConverterFactoryPtr ConvertMotionHandlerDialog::getMotionConverterFactory() {
    return motionConverterFactories[ui->chooseConverterBox->currentText().toStdString()];
}

void ConvertMotionHandlerDialog::setTargetModelLabel(const std::string &filePath) {
    QString label = QString::fromStdString(MMM::XML::getFileName(filePath));
    ui->loadedTargetModel->setText(label);
    ui->loadedTargetModel->setToolTip(label);
}

void ConvertMotionHandlerDialog::setConverterConfigLabel(const std::string &filePath) {
    QString label = QString::fromStdString(MMM::XML::getFileName(filePath));
    ui->loadedConverterConfig->setText(label);
    ui->loadedConverterConfig->setToolTip(label);
}

void ConvertMotionHandlerDialog::setConverterOptions() {
    ui->convertedMotionName->setText(QString::fromStdString("converted_" + motionName));

    if (currentMotion->getModel(false)) {
        ui->newMotionRadioButton->setChecked(true);
        ui->oldMotionRadioButton->setDisabled(true);
        ui->oldMotionRadioButton->setToolTip("To convertible motion already contains a model");
    } else {
        ui->oldMotionRadioButton->setChecked(true);
        ui->oldMotionRadioButton->setDisabled(false);
        ui->oldMotionRadioButton->setToolTip("");
    }
}

std::string ConvertMotionHandlerDialog::getMotionName() {
    if (ui->newMotionRadioButton->isChecked()) {
        std::string convertedMotionName = ui->convertedMotionName->text().toStdString();
        if (convertedMotionName.empty()) throw MMM::Exception::MMMException("Motion name for converted motion should not be empty!");
        else return convertedMotionName;
    }
    else return motionName;
}

